from market.math import sumVectorElements
from market.math import vectorScalarProduct
from market.math import isARepartition
from market.math import newton


#########################################################################################################################
####### Rule 2 Ter: Change of strategy : equity point ofview 2
#########################################################################################################################
#Instead of passing a new strategy, on can specify how many asset0 he want to sell in order to buy some asset1 and 
#keep constant number other assets.
#we have to compute the new strategy, this transaction is leading to.

#This means that we want to find stratNew_asset and nNew such that
		# 
		# for all asset nNew* stratNew_asset = n * strat_asset + eps_asset
		# 
		# sum_asset( stratNew_asset ) = 1
		# 
		# Mtm(fNew)=MtM(f)
		# 
		# where eps_asset  = 0 if asset!= asset1 and asset != asset0
		#       eps_asset0 = - value to change
		#       eps_asset1 is an unknown
		#
#We have the following solution:
		# 
		# for all asset  rNew_asset = (n* r_asset + eps_asset)/ nNew
		#
		# nNew = Sum_asset(n* r_asset + eps_asset)
		# 
#and we solve over eps_asset1 to keep Mtm(fNew)=MtM(f)
class FindNewRepRule2TerHelper:

        ###########################################################################################
        # initializes the class and assigns a value to the status string in case of failure
        def __init__( self, totalNumberOfAssetBefore, repartitionBefore, indexOfSoldAsset, numberOfSoldAsset, indexOdBoughtAsset, marketPrices, nPlayers ):
                self.myTotalNumberOfAssetBefore = float(totalNumberOfAssetBefore)
                self.myRepartitionBefore = repartitionBefore
                self.myIndexOfSoldAsset = int(indexOfSoldAsset)
                self.myNumberOfSoldAsset = float(numberOfSoldAsset)
                self.myIndexOdBoughtAsset = int(indexOdBoughtAsset)
                self.myMarketPrices = marketPrices
                self.MtMBefore = float(totalNumberOfAssetBefore) * vectorScalarProduct( repartitionBefore, marketPrices )
                self.nPlayers = float(nPlayers)
                self.initializationStatusString = ""
                # vectors have different sizes
                if len(repartitionBefore) != len(marketPrices):
                        self.initializationStatusString = "mkt prices and view does not have the same lenght"
                        return
                #the index of sold/bought asst is not valid
                if( indexOfSoldAsset > len(repartitionBefore) or indexOdBoughtAsset > len(repartitionBefore) or  indexOfSoldAsset < 0 or  indexOdBoughtAsset < 0 ):
                        self.initializationStatusString = "index of sold asset is not in the range"
                        return
                #numberOfSoldAsset must be >= 1 ( in theory 0 but, we do not want to mess with the newton and the epsDer
                if( self.myNumberOfSoldAsset != 0 and self.myNumberOfSoldAsset < 1 ):
                        self.initializationStatusString = "number of sold asset must be greater than one"
                        return
                #want to sell more asset than we have
                if( self.myNumberOfSoldAsset > self.myTotalNumberOfAssetBefore * repartitionBefore[ indexOfSoldAsset ] ):
                        self.initializationStatusString = "number of sold asset must be smaller than the number of owned asset"
                        return
                # the input input repartition is not valid
                tmp = isARepartition( repartitionBefore )
                if( not(tmp[0]) ):
                        self.initializationStatusString = "the initial repartition is not valid: " + tmp[1]
                        return
                return

        ###########################################################################################
        # find the new total number of asset and the new epartition fuction of the number of the bought assets
        def getNewRepartition( self, numberOfBoughtAsset ):
                eps = [0 for p in range(len(self.myRepartitionBefore))]
                eps[self.myIndexOfSoldAsset] = self.myNumberOfSoldAsset
                eps[self.myIndexOdBoughtAsset] = -float(numberOfBoughtAsset)
                totalNumberOfAssetAfter = self.myTotalNumberOfAssetBefore + numberOfBoughtAsset - self.myNumberOfSoldAsset
                numbersAfter = [self.myTotalNumberOfAssetBefore * self.myRepartitionBefore[p] - eps[ p ] for p in range(len(eps))]
                repartitionAfter = [numbersAfter[p]/totalNumberOfAssetAfter for p in range(len(numbersAfter))]
                return [totalNumberOfAssetAfter,repartitionAfter]

        ###########################################################################################
        # funtion to pass to the solver
        def funcToSolve( self, numberOfBoughtAsset ):
                resTmp = self.getNewRepartition(numberOfBoughtAsset)
                newMarketPrices = self.myMarketPrices[:]
                for i in range(len(newMarketPrices)):
                        deltaPrice = ( resTmp[1][i] - self.myRepartitionBefore[i])  /self.nPlayers
                        newMarketPrices[i]   = newMarketPrices[i] + deltaPrice
                return self.MtMBefore - resTmp[0] * vectorScalarProduct( resTmp[1], newMarketPrices )

        ###########################################################################################      
        # return the new repartition and the number of bought asset (just for checks purposes)
        def findNewRepRule2Ter( self ):
                if( ( self.myIndexOfSoldAsset == self.myIndexOdBoughtAsset ) or self.myNumberOfSoldAsset == 0 ):
                        return [self.myRepartitionBefore,0]
                xstart = max(self.myNumberOfSoldAsset,100)
                if( self.myMarketPrices[self.myIndexOdBoughtAsset] > 0.00001 ):
                        # this is the number of the asset bought if the price did not change during the transaction
                        # we floor it at 0.0002 in order to allow for the first derivative calculation (epsDer = 0.0001)
                        xstart = max(20,self.myNumberOfSoldAsset * self.myMarketPrices[self.myIndexOfSoldAsset]/self.myMarketPrices[self.myIndexOdBoughtAsset])
                #                     newton( xstart, func,             epsDer, tol,   maxloops ):
                numberOfBoughtAsset = newton( xstart, self.funcToSolve, 0.0001, 0.001, 50       )
                resTmp = self.getNewRepartition(numberOfBoughtAsset)
                return [resTmp[1],numberOfBoughtAsset]
                
            
